﻿// Deep Earth is a community project available under the Microsoft Public License (Ms-PL)
// Code is provided as is and with no warranty – Use at your own risk
// View the project and the latest code at http://DeepEarth.codeplex.com/

using System;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using GeoAPI.Geometries;

namespace DeepEarth.Client.MapControl.Geometries
{
    //public class GeometryBase<TGeometry> : Control
    public abstract class GeometryControlBase<TGeometry> : Control where TGeometry : IGeometry
    {
        /// <summary>
        /// Static DependencyProperty for receiving the geometry anchor value from XAML instantiation.
        /// </summary>
        public static readonly DependencyProperty AnchorPointProperty = DependencyProperty.Register("AnchorPoint",
            typeof(Point), typeof(GeometryControlBase<TGeometry>), new PropertyMetadata(new PropertyChangedCallback(OnAnchorPointChanged)));

        private bool _isAnchorCalculated;
        private bool _isInitialVisibilitySet;
        private Size _originalSize;

        private TGeometry _geometryItem;
        public TGeometry GeometryItem
        {
            get { return _geometryItem; }
            set
            {
                if (_geometryItem == null || _geometryItem.CompareTo(value) != 0)
                {
                    _geometryItem = value;
                    UpdateLocation();
                }
            }
        }

        internal abstract void UpdateLocation();

        ///<summary>
        /// Initializes the an instance of the GeometryBase.
        ///</summary>
        protected GeometryControlBase(TGeometry geometry)
        {
            _id = Guid.NewGuid();
            GeometryItem = geometry;
        }

        /// <summary>
        /// The globally unique identifier for the geometry
        /// </summary>
        public Guid Id
        {
            get
            {
                if (_id == Guid.Empty) _id = Guid.NewGuid();
                return _id;
            }

        }
        private Guid _id = Guid.Empty;

        /// <summary>
        /// Does the geometry intersect with the bounds of the current screen's view port.
        /// </summary>
        public bool InView { get; set; }

        /// <summary>
        /// Has the template been applied to the shape.
        /// </summary>
        public bool IsLoaded { get; protected set; }

        /// <summary>
        /// The parent GeometryLayer reposible for the positioning layout and control.
        /// </summary>
        public GeometryLayer Layer { get; internal set; }

        /// <summary>
        /// Indicates whether the geometry currently visible.
        /// </summary>
        public bool IsVisible
        {
            get { return _isVisible; }
            set
            {
                if (_isInitialVisibilitySet == false || _isVisible != value)
                {
                    _isVisible = value;
                    _isInitialVisibilitySet = true;
                }
            }
        }
        private bool _isVisible = true;

        /// <summary>
        /// A point within the geometry extent to anchored it's position on the canvas.
        /// </summary>
        public Point AnchorPoint
        {
            get { return _anchorPoint; }
            set
            {
                _isAnchorCalculated = false;
                _anchorPoint = value;
                Refresh();
            }
        }
        private Point _anchorPoint;

        /// <summary>
        /// For the ScaleTransform method the entire canvas is scaled to sale the child objects.  
        /// For elements which should not scales, such as the border
        /// thickness we use the ScaleAdjustment as an opportunity to offset the scaling and keeps visual element size constant.
        /// </summary>
        public virtual double ScaleAdjustment
        {
            get { return _scaleAdjustment; }
            set
            {
                _scaleAdjustment = value;
            }
        }
        private double _scaleAdjustment = 1;

        /// <summary>
        /// A Point ranging from (0,0) UpperLeft to (1,1) BottomRight within the extent if the geometry item to anchor to the canvas.
        /// </summary>
        public Point Anchor
        {
            get
            {
                if (_isAnchorCalculated == false)
                {
                    if (IsDimensionsConfigured())
                    {
                        if (AnchorPoint == new Point())
                        {
                            _anchor = new Point((Width / 2), (Height / 2));
                        }
                        else
                        {
                            _anchor = new Point(Width * AnchorPoint.X, Height * AnchorPoint.Y);
                        }

                        _isAnchorCalculated = true;
                    }
                }

                if (double.IsNaN(_anchor.X) || double.IsNaN(_anchor.Y)) _anchor = new Point();

                return _anchor;
            }
        }
        private Point _anchor;

        /// <summary>
        /// Specifies whether or not the dimensions of the Geometry are configured
        /// </summary>
        /// <returns>Boolean</returns>
        private bool IsDimensionsConfigured()
        {
            var isConfigured = false;
            if ((double.IsNaN(Width) == false) && (double.IsNaN(Height) == false))
            {
                if (Width > 0 && Height > 0)
                {
                    isConfigured = true;
                }
            }
            return isConfigured;
        }

        /// <summary>
        /// Map Instance access
        /// </summary>
        public Map MapInstance
        {
            get
            {
                if (Layer == null)
                {
                    return null;
                }
                return Layer.MapInstance;
            }
        }

        /// <summary>
        /// Handles the event raised by applying the FrameworkElement template
        /// </summary>
        public override void OnApplyTemplate()
        {
            // Test IsDesignTime, to help display control in blend correctly
            if (!DesignerProperties.IsInDesignTool)
            {
                IsLoaded = true;
                ForceMeasure();
                Layer.UpdateChildLocation(this as GeometryControlBase<IGeometry>);
            }
        }

        /// <summary>
        /// Force a resizing measure to determine the width and height
        /// </summary>
        public void ForceMeasure()
        {
            // force measure if the width or height is NaN to determine what the width or height should be
            if (double.IsNaN(Width) || double.IsNaN(Height))
            {
                Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
                _originalSize = new Size(DesiredSize.Width, DesiredSize.Height);
            }
            else
            {
                _originalSize = new Size(Width, Height);
            }

            Width = _originalSize.Width;
            Height = _originalSize.Height;
        }

        /// <summary>
        /// Refresh the location of the Geometry within its containing layer
        /// </summary>
        protected void Refresh()
        {
            // TODO: Why is LAYER & MAPINSTANCE == NULL ????
            if (Layer != null && MapInstance != null)
            {
                Layer.UpdateShape(this as GeometryControlBase<IGeometry>);
            }
        }

        /// <summary>
        /// Determines whether or not the geometry shares an overlap with another geometry
        /// </summary>
        /// <param name="bounds">A rectangle to intersect with</param>
        /// <returns>Boolean</returns>
        public virtual bool Intersects(Rect bounds)
        {
            // TODO: Implement this Method
            // bounds.Contains(point);
            return true;
        }

        private static void OnAnchorPointChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ((GeometryControlBase<TGeometry>)d).AnchorPoint = (Point)e.NewValue;
        }
    }
}